/**
 * 
 */
package jnzb.control.queue;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import jnzb.model.nzb.NZB;
import jnzb.model.queue.WaitingTableModel;

/**
 * Manages all aspects of the NZB queue.
 * 
 * @author AA1
 * 
 */
public class QueueManager {

	private LinkedList<NZB> queue;
	private WaitingTableModel queueTableModel = new WaitingTableModel();
	private ArrayList<QueueChangeListener> queueChangedListeners;

	public QueueManager() {
		queue = new LinkedList<NZB>();
		queueChangedListeners = new ArrayList<QueueChangeListener>();

		// Set the underlying queue into the table model
		queueTableModel.setQueue(this.queue);
	}

	/**
	 * Returns the next NZB eligible for download
	 * 
	 * @return
	 */
	public NZB getNextNZB() {
		return queue.isEmpty()?null:queue.getFirst();
	}

	/**
	 * Adds an NZB to the back of the queue
	 * 
	 * @param nzb
	 */
	public void addNZB(NZB nzb) {
		if (!alreadyContainsFile(nzb)) {
			this.queue.addLast(nzb);
			notifyListeners();
		}
	}

	/**
	 * Checks that there isnt an NZB with the same filename as the one passed
	 * in. Decided to do it like this instead of overriding equals in NZB as
	 * it's a very simple comparison
	 * 
	 * @param nzb
	 * @return true if the filename is found in the current queue of nzbs
	 */
	private boolean alreadyContainsFile(NZB nzb) {
		String filename = nzb.getFilename().toLowerCase();

		Iterator<NZB> i = queue.iterator();
		while (i.hasNext()) {
			if (i.next().getFilename().toLowerCase().equals(filename)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Removes the current NZB
	 * 
	 * @param nzb
	 */
	public void removeNZB(NZB nzb) {
		this.queue.remove(nzb);
		notifyListeners();
	}

	/**
	 * Check if the NZB queue is empty
	 * 
	 * @return true if empty
	 */
	public boolean isQueueEmpty() {
		return this.queue.isEmpty();
	}

	/**
	 * Returns the table model for the queue
	 * 
	 * @return
	 */
	public WaitingTableModel getTableModel() {
		return this.queueTableModel;
	}

	/**
	 * Returns the NZB that is at a specific index.
	 * 
	 * @param index
	 * @return
	 */
	public NZB getNZBAt(int index) {
		return queue.get(index);
	}

	/**
	 * Moves an NZB up the queue by 1 position
	 * 
	 * @param nzb
	 * @return the position after the queue change
	 */
	public int moveUp(NZB nzb) {
		int pos=-1;
		// If its in the queue and its not the first one
		if (queue.contains(nzb) && !queue.getFirst().equals(nzb)) {
			// Find out it's current position
			int currentPos = queue.indexOf(nzb);
			queue.remove(nzb);
			queue.add(currentPos - 1, nzb);
			notifyListeners();
			pos=currentPos-1;
		}
		return pos;
	}

	/**
	 * Moves and NZB down the queue by 1 position
	 * 
	 * @param nzb
	 */
	public int moveDown(NZB nzb) {
		int pos=-1;
		// If its in the queue and its not the last one
		if (queue.contains(nzb) && !queue.getLast().equals(nzb)) {
			// Find out it's current position
			int currentPos = queue.indexOf(nzb);
			queue.remove(nzb);
			queue.add(currentPos + 1, nzb);
			notifyListeners();
			pos=currentPos+1;
		}
		return pos;
	}

	/**
	 * Returns the size of the nzb queue
	 * 
	 * @return
	 */
	public int getQueueSize() {
		return queue.size();
	}

	/**
	 * Register an interest in changes to the queue
	 * 
	 * @param qcl
	 */
	public void registerListener(QueueChangeListener qcl) {
		this.queueChangedListeners.add(qcl);
	}

	/**
	 * Notify any listeners of a change to the queue & trigger the underlying
	 * table change event
	 */
	protected void notifyListeners() {
		this.queueTableModel.fireTableDataChanged();
		Iterator<QueueChangeListener> listeners = queueChangedListeners
				.iterator();

		while (listeners.hasNext()) {
			listeners.next().queueChanged();
		}
	}
}
